<?php

namespace frontend\controllers;

use common\models\TActivity;
use common\models\TOrder;
use common\models\TPayLog;
use common\models\TSession;
use common\models\TSignUp;
use Yii;
use yii\web\Controller;
use yii\web\Cookie;

class PayController extends Controller{
    public $layout = false;
    public $enableCsrfValidation = false;
    public function actionCallback(){
        $xml = file_get_contents("php://input");
        $data = $this->xmlToArray($xml);

        $sign = $data['sign'];
        unset($data['sign']);
        $newSign = $this->makeSign($data);

        if ($sign == $newSign && $data['result_code'] == 'SUCCESS') {
            // 处理订单
            $trans = Yii::$app->db->beginTransaction();
            try {
                $order = TOrder::find()->where(['orderSn' => $data['out_trade_no']])->one();
                if ($order && $order['status'] == TOrder::STATUS_INIT && $order['payStatus'] == TOrder::PAY_STATUS_INIT) {
                    $order->status = TOrder::STATUS_FINISH;
                    $order->payStatus = TOrder::PAY_STATUS_PAYED;
                    $order->payTime = time();
                    $order->_uptm = time();
                    $order->save(false);
                    $log = TPayLog::find()->where(['fk' => $order->id, 'type' => TPayLog::TYPE_ORDER])->one();
                    if($log && !$log['paymentNo']){
                        $log->paymentNo = $data['transaction_id'];
                        $log->update(false);
                    }else{
                        Yii::error("支付日志不存在或交易异常,paymentNo:{$log['paymentNo']}, fk:{$order->id}");
                    }
                    $signUp = TSignUp::findOne($order->signUpId);
                    if($signUp && $signUp->status == TSignUp::STATUS_INIT){
                        $signUp->status = TSignUp::STATUS_PAYED;
                        $signUp->save(false);
                    }else{
                        Yii::error("报名信息不存在或状态异常,status:{$signUp->status}, id:{$order->signUpId}");
                    }
                    $trans->commit();
                }
            }catch (\Exception $e){
                $trans->rollBack();
                Yii::error([$data, $e->getMessage()], __METHOD__);
            }
            $response = [
                'return_code' => 'SUCCESS',
                'return_msg' => 'OK'
            ];
        } else {
            $response = [
                'return_code' => 'FAIL',
                'return_msg' => '签名失败'
            ];
        }

        echo $this->arrayToXml($response);
        Yii::info([$_GET, $_POST, Yii::$app->request->getRawBody(), $response], __METHOD__);
    }


    /**
     * 活动确认
     * @param $signUpId
     * @return string|void
     */
    public function actionConfirm($signUpId){
        $signUp = TSignUp::findOne($signUpId);
        if(!$signUp){
            return '无效的活动报名id';
        }
        $session = TSession::findOne($signUp->sessionId);
        if(!$session){
            $session = ['name' => '预选赛', 'price' => Yii::$app->params['personMoney']];
        }
        $money = number_format($session['price'] * $signUp->persons, 2, '.', '');
        $token = Yii::$app->getSecurity()->generateRandomString();
        $cookie = $this->createCsrfCookie($token);
        Yii::$app->getResponse()->getCookies()->add($cookie);
        return $this->render('confirm',  [
            'money' => $money,
            'persons' => $signUp->persons,
            'tips' => "{$session['price']} * {$signUp->persons} = {$money}",
            'signUpId' => $signUpId,
            'token' => $token
        ]);

    }

    /**
     * 微信支付
     * @return \yii\web\Response
     * @throws \Exception
     */
    public function actionOrder(){
        $signUpId = Yii::$app->request->post('signUpId');
        $token    = Yii::$app->request->post('token');
        $csrfToken = $this->getCsrfCookie();
        if($token !== $csrfToken){
            return $this->asJson(['code' => 1, 'msg' => "无效的请求，请刷新页面后重试"]);
        }
        $cacheValue = Yii::$app->cache->get($token);
        if($cacheValue){
            return $this->asJson(['code' => 1, 'msg' => "无效的请求，请刷新页面后重试"]);
        }
        $signUp = TSignUp::findOne($signUpId);
        if(!$signUp){
            return $this->asJson(['code' => 1, 'msg' => "无效的报名信息"]);
        }

        $activity = TActivity::findOne($signUp->activityId);
        if(!$activity){
            $activity = ['name' => '燕赵文化银龄展演'];
        }
        $session = TSession::findOne($signUp->sessionId);
        if(!$session){
            $session = ['name' => $activity['name'].'预选赛', 'price' => Yii::$app->params['personMoney']];
        }
        $nonce_str = $this->createNonceStr();
        $out_trade_no = date('YmdHis') . mt_rand(1000, 9999);
        $price =  $session['price']* $signUp->persons;
        $orderData = [
            'orderSn'  => $out_trade_no,
            'signUpId' => $signUpId,
            'subject'  => "{$activity['name']}{$session['name']}报名费",
            'price'    => $price,
            'payPrice' => $price,
            'status'   => TOrder::STATUS_INIT,
            'payStatus'=> TOrder::PAY_STATUS_INIT,
            '_intm'    => time(),
            '_uptm'    => time(),
        ];
        $order = new TOrder();
        $order->load($orderData, '');
        if($order->save()){
            $logData = ['fk' => $order->id,'type' => TPayLog::TYPE_ORDER, 'amount' => $orderData['price'], 'transactionNo' => $out_trade_no,
                'paymentNo' => '', 'requestContent' => '', 'msg' => '', '_intm' => time(), '_uptm' => time()];
            $log = new TPayLog();
            $log->load($logData, '');
            $log->save(false);
            $params = [
                'appid' => Yii::$app->params['payment']['appId'],
                'mch_id' => Yii::$app->params['payment']['mchId'],
                'nonce_str' => $nonce_str,
                'body' => $orderData['subject'],
                'out_trade_no' => $out_trade_no,
                'total_fee' => intval($orderData['price'] * 100),
                'spbill_create_ip' =>'121.43.48.92',
                'notify_url' => Yii::$app->params['payment']['notifyUrl'],
                'trade_type' => 'JSAPI',
                'openid' => 'oQvoG7MsD_2Ljuy5WYkKImnB6J0I'
            ];

            $params['sign'] = $this->makeSign($params);
            $xmlData = $this->arrayToXml($params);
            $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
            $response = $this->postXmlCurl($xmlData, $url);

            $result = $this->xmlToArray($response);

            if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
                $prepay_id = $result['prepay_id'];

                // 返回 JSAPI 调起支付参数
                $jsapiParams = [
                    'appId' => Yii::$app->params['payment']['appId'],
                    'timeStamp' => strval(time()),
                    'nonceStr' => $this->createNonceStr(),
                    'package' => "prepay_id=$prepay_id",
                    'signType' => 'MD5',
                ];
                $jsapiParams['paySign'] = $this->makeSign($jsapiParams);
                Yii::$app->cache->set($token, 1, 86400);
                return $this->asJson(['code' => 0, 'data' => $jsapiParams]);
            } else {
                return $this->asJson(['code' => 1, 'data' => $result]);
            }
        }else{
            return $this->asJson(['code' => 1, 'data' => '创建支付订单失败']);
        }
    }
    /**
     * 生成随机字符串
     * @param int $length
     * @return string
     */
    private function createNonceStr($length = 32)
    {
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $str = '';
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * 生成签名
     * @param array $params
     * @return string
     */
    private function makeSign($params)
    {
        // 过滤空值
        $params = array_filter($params);

        // 按字典序排序参数
        ksort($params);

        // 生成URL键值对格式
        $string = '';
        foreach ($params as $k => $v) {
            $string .= $k . '=' . $v . '&';
        }

        // 拼接API密钥
        $string .= 'key=' . Yii::$app->params['payment']['apiKey'];

        // MD5加密并转为大写
        return strtoupper(md5($string));
    }

    /**
     * 数组转XML
     * @param array $arr
     * @return string
     */
    private function arrayToXml($arr)
    {
        $xml = '<xml>';
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= '<' . $key . '>' . $val . '</' . $key . '>';
            } else {
                $xml .= '<' . $key . '><![CDATA[' . $val . ']]></' . $key . '>';
            }
        }
        $xml .= '</xml>';
        return $xml;
    }

    /**
     * XML转数组
     * @param string $xml
     * @return array
     */
    private function xmlToArray($xml)
    {
        $arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $arr;
    }

    /**
     * 以post方式提交xml到对应的接口url
     * @param string $xml 需要post的xml数据
     * @param string $url url
     * @param bool $useCert 是否需要证书，默认不需要
     * @param int $second url执行超时时间，默认30s
     * @return mixed
     */
    private function postXmlCurl($xml, $url, $useCert = false, $second = 30)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);

        // 设置证书
        if ($useCert == true) {
            curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLCERT, Yii::$app->params['payment']['sslcert_path']);
            curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLKEY, Yii::$app->params['payment']['sslkey_path']);
        }

        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);

        $data = curl_exec($ch);

        if ($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            throw new \Exception("curl出错，错误码:$error");
        }
    }

    /**
     * @param $token
     * @return Cookie
     * @throws \yii\base\InvalidConfigException
     */
    protected function createCsrfCookie($token)
    {
        return Yii::createObject(array_merge(['httpOnly' => true], [
            'class' => 'yii\web\Cookie',
            'name' => 'h5pay',
            'value' => $token,
        ]));
    }

    protected function getCsrfCookie(){
        return Yii::$app->request->getCookies()->getValue('h5pay');
    }
}